#include <fstream>
#include <algorithm>
#include <vector>
#include <iostream>
#include <assert.h>
#include <time.h>
#include <opencv2/opencv.hpp>

using namespace std;

const int max_height = 600;
const int max_width = 1500;

class what{
public:
    what(int i, int j) {value = i; count = j;}
    what(){}
    int value;
    int count;
    bool operator() (what i,what j) { return (i.value<j.value);}
};

class table_entry{
public:
    class pair_c{
    public:
        pair_c(int s, int e) :start(s), end(e) {}
        pair_c(const pair_c &copy):start(copy.start),end(copy.end) {}
        //data fileds
        int start, end;
    };

    table_entry(int num_start, int num_end, int value_start, int value_end)
            : num(num_start, num_end), value(value_start, value_end){}

    table_entry(pair_c arg_num, pair_c arg_value):num(arg_num),value(arg_value) {}

    //data fileds
    pair_c num, value;
};

int initial_values[6][4] =  {{0,     230,    0,  5},
                            {230,   300,    5,  40},
                            {300,   500,    40, 60},
                            {500,   850,    60, 85},
                            {850,   1000,   85, 90},
                            {1000,  1500,   95, 100}};

const table_entry &belong_to(const vector<table_entry> &table, int num) {
    //cout << "belong_to(table, " << num << ")" << endl;
    for(vector<table_entry>::const_iterator iter = table.begin(); iter < table.end(); iter ++) {
        if( iter->num.start <= num && num <= iter->num.end) {
            return *iter;
        }
    }
    cout << "belong_to(num = " << num << ") !!!!, not found !!!" << endl;
    return *(table.end() - 1);
}

int count_num(const vector<int> &vect, int num) {
    int count = 0;
    for(unsigned int i = 0;i < vect.size(); i ++) {
        if(vect[i] == num) {
            count ++;    
        }
    }
    return count;
}

int count_sorted_le(const vector<what> &vect, const int num) {
    int count = 0;
    for(vector<what>::const_iterator iter = vect.begin(); iter->value <= num; iter ++) {
        count += iter->count;
    }
    
    return count;
}

IplImage *drawHistImage(IplImage *img, const vector<int> &vect) {

    what w;
    vector<what> real;
    vector<int> iVect = vect;

    sort(iVect.begin(), iVect.end());

    for(unsigned int count = 0; count < iVect.size(); count ++) {
        //cout << iVect[count] << '\t' << endl;
    }

    int old = *(iVect.begin()) - 1;
    int max_count = 0;
    int sum_count = 0;
    for(vector<int>::const_iterator iter=iVect.begin(); iter < iVect.end(); iter ++) {
        if(old != *iter) {
            
            w.value = *iter; 
            w.count = count_num(iVect, *iter);
            real.push_back(w);
            sum_count += w.count;
            max_count = max_count < w.count ? w.count : max_count;
            //cout << w.value << ", count=" << w.count <<endl;
        }
        old = *iter;
    }

    const int sub = ((real.end() -1) ->value - real.begin()->value);
    const float min_width = (float(max_width)) / (sub + 1);         //sub +1 
    const float min_height = ((float) max_height) / 3 * 2 / max_count;          //以使最高为max_height的2/3

    if( sub + 1 > max_width || max_count > max_height/3*2) {
        //cout << "[WARNING!!!!!] max_height || max_width not enough !!!!!!!!!!!!" << endl;
    }

    /*
    cout << "max_width=" << max_width << ", max_height=" << max_height
         << ", min_width=" << min_width << ", min_height=" << min_height 
         << ", max_count=" << max_count <<endl;
    */

    cvZero(img);

    srand((unsigned) time(NULL));
    int i = 0;
    int value_r, value_g, value_b;
    int value_all = rand() % 255;
    for(vector<what>::iterator iter=real.begin(); iter<real.end();  i++) {
        if( i + real.begin()->value != iter->value) {
            continue;
        }

        CvPoint p1, p2;
        p1.x = cvRound (i * min_width);
        p1.y = max_height - cvRound(min_height * iter->count);

        p2.x = cvRound (p1.x + min_width);
        p2.y = max_height;
        
        value_r = (value_all += 200 ) % 255;
        value_g = (value_all += 200 ) % 255;
        value_b = (value_all += 200 ) % 255;
        cvRectangle(img,  p1, p2, cvScalar(value_r, value_g, value_b), -1/*, int line_type=8, int shift=0*/ );
        //cout << "iter->value = " << iter->value << ", p2=(" << p2.x << "," << p2.y << "), i = " << i << endl;

        iter ++;
    }

    char text[512];
    sprintf(text, "image size(%d*%d), all=%d, X-axis:(%d--%d), Y-axis:(0--%d)", 
            max_width, max_height, sum_count, real.begin()->value, (real.end()-1)->value, max_count);
    CvFont font;
    cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 1.0f,1.0f);
    cvPutText(img, text, cvPoint(0,64), &font, cvScalar(255,255,255));

    cvInitFont(&font, CV_FONT_HERSHEY_PLAIN, 1.0f, 1.0f, 0, 2);
    float percent = 0;
    for(int i_old, i = 0; i <= sub; i ++) {
        if( (0 == i) || ((i - i_old) * min_width >= 100)) {
            sprintf(text, "| %d", i+real.begin()->value);
            cvPutText(img, text, cvPoint(i*min_width, max_height - 20), &font, cvScalar(255,255,255));
            sprintf(text, "| %d.%d%%", int(percent*100), ((int) (percent*1000))%10);
            cvPutText(img, text, cvPoint(i*min_width, max_height - 5), &font, cvScalar(255,255,255));
            i_old = i;
            percent = (float) count_sorted_le( real, i+real.begin()->value)/sum_count;
        }
    }

    return img;
}

int main(int argc, char **argv) {

    ifstream ifs;
    string avg_begin("avg>>>>>"), avg_end("<<<<<avg");
    string nearest_begin("nearest>>>>>"), nearest_end("<<<<<nearest");
    string mostly_like_begin("mostly_like>>>>>"), mostly_like_end("<<<<<mostly_like");
    string benchmark_begin("benchmark>>>>>"), benchmark_end("<<<<<benchmark");
    string clear_all_values(">>>>>CLEAR_ALL_VALUES<<<<<");

    string window_name_avg(" avg ");
    string window_name_nearest(" nearest ");
    string window_name_mostly_like(" mostly_like ");
    string window_name_benchmark(" benchmark" );

    vector<int> vect_avg, vect_nearest, vect_mostly_like, vect_benchmark;
    vector<table_entry> table;
    for(unsigned int i = 0; i < sizeof(initial_values) / sizeof(initial_values[0]); i ++) {
        table.push_back(table_entry(initial_values[i][0], initial_values[i][1], initial_values[i][2], initial_values[i][3]));
    }

    /*
    for(vector<table_entry>::const_iterator iter = table.begin(); iter < table.end(); iter ++) {
        cout << "table entry:" << iter->num.start << "," << iter->num.end << "," << iter->value.start << "," << iter->value.end<< endl;
    }
    */

    if(argc == 1) {
        cout << "will reciver data from stdin, " << endl 
                <<"\tusage: adb logcat | " << argv[0] << endl;
    } else if(argc == 2) {
        cout << "will read data from " << argv[1] << endl;
        ifs.open(argv[1]);
        window_name_avg = window_name_avg + "(" + argv[1] + ")";
        window_name_nearest = window_name_nearest+ "(" + argv[1] + ")";
        window_name_mostly_like= window_name_mostly_like+ "(" + argv[1] + ")";
        window_name_benchmark = window_name_benchmark + "(" + argv[1] + ")";
    } else {
        return 0;
    }

    cvNamedWindow(window_name_avg.c_str(), CV_WINDOW_AUTOSIZE);
    cvNamedWindow(window_name_nearest.c_str(), CV_WINDOW_AUTOSIZE);
    cvNamedWindow(window_name_mostly_like.c_str(), CV_WINDOW_AUTOSIZE);
    cvNamedWindow(window_name_benchmark.c_str(), CV_WINDOW_AUTOSIZE);

    IplImage *img_avg = cvCreateImage(cvSize(max_width, max_height), IPL_DEPTH_8U, 3);
    IplImage *img_nearest = cvCreateImage(cvSize(max_width, max_height), IPL_DEPTH_8U, 3);
    IplImage *img_mostly_like = cvCreateImage(cvSize(max_width, max_height), IPL_DEPTH_8U, 3);
    IplImage *img_benchmark= cvCreateImage(cvSize(max_width, max_height), IPL_DEPTH_8U, 3);

    char cbuffer[1024];
    string sbuffer;
    int avg=0, nearest=0, mostly_like=0, benchmark = 0;

    bool bRunning = true;
    int mSeconds = 5;
    while(true) {
        if(1 == argc) {
            cin.getline(cbuffer, sizeof(cbuffer));
            if(cin.eof()) {
                cout << "eof" << endl;
                break;
            }
        } else if( 2 == argc) {
            ifs.getline(cbuffer, sizeof(cbuffer));
            if(ifs.eof()) {
                cout << "eof" << endl;
                break;
            }
        }

        sbuffer = cbuffer;
        if(sbuffer.size() <= 0) {
            continue;
        }
        //cout << "readed: " << sbuffer << endl;

        if(sbuffer.find(clear_all_values) != sbuffer.npos) {
            vect_avg.clear();
            vect_nearest.clear();
            vect_mostly_like.clear();
            vect_benchmark.clear();
            cout << "All readed Data Cleared" << endl;
            continue;
        }

        size_t iBegin = sbuffer.find(avg_begin);
        if(iBegin != sbuffer.npos) {
            iBegin += avg_begin.size();
            avg = atoi(sbuffer.substr(iBegin, sbuffer.find(avg_end) - iBegin).c_str());
            vect_avg.push_back(avg);

            iBegin = sbuffer.find(nearest_begin) + nearest_begin.size();
            nearest = atoi(sbuffer.substr(iBegin, sbuffer.find(nearest_end) - iBegin).c_str());
            vect_nearest.push_back(nearest);

            iBegin = sbuffer.find(mostly_like_begin) + mostly_like_begin.size();
            mostly_like = atoi(sbuffer.substr(iBegin, sbuffer.find(mostly_like_end) - iBegin).c_str());
            vect_mostly_like.push_back(mostly_like);

            iBegin = sbuffer.find(benchmark_begin) + benchmark_begin.size();
            benchmark = atoi(sbuffer.substr(iBegin, sbuffer.find(benchmark_end) - iBegin).c_str());
            /* read from stream insteaded
            const table_entry &e = belong_to(table, nearest);
            benchmark = cvRound(((float)(nearest - e.num.start))/(e.num.end - e.num.start) * (e.value.end - e.value.start) + e.value.start);
            */
            vect_benchmark.push_back(benchmark);

            //cout << "avg = " << avg << ", nearest = " << nearest << ", mostly_like = " << mostly_like << endl;

            img_avg = drawHistImage(img_avg, vect_avg);
            img_nearest= drawHistImage(img_nearest, vect_nearest) ;
            img_mostly_like = drawHistImage(img_mostly_like, vect_mostly_like) ;
            img_benchmark = drawHistImage(img_benchmark, vect_benchmark);

            cvShowImage(window_name_avg.c_str(), img_avg);
            cvShowImage(window_name_nearest.c_str(), img_nearest);
            cvShowImage(window_name_mostly_like.c_str(), img_mostly_like);
            cvShowImage(window_name_benchmark.c_str(), img_benchmark);

            int kp;
            while(true) {
                if((kp = cvWaitKey(mSeconds)) == 32) {
                    bRunning = !bRunning;
                    if(bRunning) {
                        mSeconds = 5;
                        cout << "!!!!!!!!!!!!!!!!!!! RUNNING !!!!!!!!!!!!!!!!!!!!" << endl;
                        break;
                    } else {
                        mSeconds = INT_MAX;
                        cout << "!!!!!!!!!!!!!!!!!!! PAUSE !!!!!!!!!!!!!!!!!!!!" << endl;
                        continue;
                    }
                } else if (kp == 27) {
                    return 0;
                } else {
                    break;
                }

            }
        }

    }

    while(true) {
        if(cvWaitKey() == 27) {
            break;
        }
    }

    return 0;
}
